package com.dream21th.flow.biz;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dream21th.common.utils.AuthenticationUtil;
import com.dream21th.flow.common.Contansts;
import com.dream21th.flow.common.dto.FlowCommitDataDTO;
import com.dream21th.flow.common.dto.FlowStartDataDTO;
import com.dream21th.flow.common.dto.PageDTO;
import com.dream21th.flow.common.dto.QueryModelRespDTO;
import com.dream21th.flow.common.enums.RtnEnum;
import com.dream21th.flow.common.exception.CommonException;
import com.dream21th.flow.entity.ActHiComment;
import com.dream21th.flow.service.ActHiCommentService;
import com.dream21th.flow.service.impl.FlowProcessDiagramGenerator;
import com.dream21th.flow.utils.LocalDateUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.util.IoUtil;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.ui.common.util.XmlUtil;
import org.flowable.validation.ProcessValidator;
import org.flowable.validation.ProcessValidatorFactory;
import org.flowable.validation.ValidationError;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

@Slf4j
@Component
public class FlowableBiz {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ActHiCommentService actHiCommentService;

    @Autowired
    private FlowProcessDiagramGenerator flowProcessDiagramGenerator;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private TaskService taskService;

    /**
     * 待办查询
     *
     * @param query
     * @return
     */
    public IPage<Map<String, Object>> applyTasksPage(PageDTO query) {
        Map condition = Optional.ofNullable(query.getCondition()).orElseGet(HashMap::new);
        return transfer(actHiCommentService.applyTasksPage(query,  AuthenticationUtil.getCurrentUsername(), (String) condition.get(Contansts.SEARCH), (String) condition.get(Contansts.PROCESS_KEY)));
    }

    /**
     * 已办查询
     *
     * @param query
     * @return
     */
    public IPage<QueryModelRespDTO> getDeployModelPage(PageDTO query) {
        Map condition = Optional.ofNullable(query.getCondition()).orElseGet(HashMap::new);
        return actHiCommentService.getDeployModelPage(query, condition);
    }

    /**
     * 已办查询
     *
     * @param query
     * @return
     */
    public IPage<Map<String, Object>> applyedTasksPage(PageDTO query) {
        Map condition = Optional.ofNullable(query.getCondition()).orElseGet(HashMap::new);
        return transfer(actHiCommentService.applyedTasksPage(query, AuthenticationUtil.getCurrentUsername(), (String) condition.get(Contansts.SEARCH), (String) condition.get(Contansts.PROCESS_KEY)));
    }

    /**
     * 查询待领取任务列表
     *
     * @param query
     * @return
     */
    public IPage<Map<String, Object>> toAssignTasksPage(PageDTO query) {
        Map condition = Optional.ofNullable(query.getCondition()).orElseGet(HashMap::new);
        return transfer(actHiCommentService.toAssignTasksPage(query,  AuthenticationUtil.getCurrentUsername(), (String) condition.get(Contansts.SEARCH), (String) condition.get(Contansts.PROCESS_KEY)));
    }

    /**
     * 流程部署
     *
     * @param file
     */
    public void deploy(MultipartFile file) {
        try {
            XMLInputFactory xif = XmlUtil.createSafeXmlInputFactory();
            InputStreamReader xmlIn = new InputStreamReader(file.getInputStream(), "UTF-8");
            XMLStreamReader xtr = xif.createXMLStreamReader(xmlIn);
            BpmnXMLConverter bpmnXmlConverter = new BpmnXMLConverter();
            BpmnModel bpmnModel = bpmnXmlConverter.convertToBpmnModel(xtr);
            //模板验证
            ProcessValidator validator = new ProcessValidatorFactory().createDefaultProcessValidator();
            List<ValidationError> errors = validator.validate(bpmnModel);
            if (CollectionUtils.isNotEmpty(errors)) {
                StringBuffer es = new StringBuffer();
                errors.forEach(ve -> es.append(ve.toString()).append("/n"));
                throw  new CommonException("500","模板验证失败，原因: " + es.toString());
            }
           repositoryService.createDeployment()
                    .category("1000")
                    .key(bpmnModel.getProcesses().get(0).getId())
                    .addBpmnModel(bpmnModel.getProcesses().get(0).getName()+".bpmn",bpmnModel)
                    .deploy();
        } catch (IOException e) {
            log.error("部署流程出错:{}", e);
            throw new CommonException(RtnEnum.FLOW_DEPLOY_ERROR);
        }catch (Exception e){
            log.error("部署流程出错:{}", e);
            throw new CommonException(RtnEnum.FLOW_DEPLOY_ERROR);
        }
    }

    /**
     * 启动流程实例
     *
     * @param flowDataDTO
     * @return
     */
    public String startProcessInstance(FlowStartDataDTO flowDataDTO) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(flowDataDTO.getFlowDefKey(), flowDataDTO.getVariables());
        Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        taskService.setVariableLocal(task.getId(), Contansts.BUSNIESS_DATA, JSONObject.toJSONString(flowDataDTO.getBizData()));
        saveComment(processInstance.getId(), task.getId(), JSONObject.toJSONString(flowDataDTO.getVariables()),  AuthenticationUtil.getCurrentUsername());
        return processInstance.getId();
    }

    /**
     * 任务提交
     *
     * @param flowCommitDataDTO
     */
    public void completeTask(FlowCommitDataDTO flowCommitDataDTO) {
        String taskId = flowCommitDataDTO.getTaskId();
        String processId = flowCommitDataDTO.getProcessId();
        if (Objects.isNull(taskId)) {
            Task task = taskService.createTaskQuery().processInstanceId(processId).singleResult();
            taskId = task.getId();
        }
        saveComment(processId, taskId, JSONObject.toJSONString(flowCommitDataDTO.getVariables()), AuthenticationUtil.getCurrentUsername());
        taskService.setVariableLocal(taskId, Contansts.BUSNIESS_DATA, JSONObject.toJSONString(flowCommitDataDTO.getBizData()));
        taskService.complete(taskId, flowCommitDataDTO.getVariables());
    }

    public void assignTask(FlowCommitDataDTO flowCommitDataDTO){
        taskService.setAssignee(flowCommitDataDTO.getTaskId(),flowCommitDataDTO.getUserId());
    }
    /**
     * 获取流程图
     *
     * @param processId
     * @return
     */
    public byte[] flowImage(String processId) {
        //1.获取当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        String processDefinitionId = null;
        List<String> activeActivityIds = new ArrayList<>();
        List<String> highLightedFlows = new ArrayList<>();
        //2.获取所有的历史轨迹线对象
        List<HistoricActivityInstance> historicSquenceFlows = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processId).activityType(BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW).list();
        historicSquenceFlows.forEach(historicActivityInstance -> highLightedFlows.add(historicActivityInstance.getActivityId()));
        //3. 获取流程定义id和高亮的节点id
        if (processInstance != null) {
            //3.1. 正在运行的流程实例
            processDefinitionId = processInstance.getProcessDefinitionId();
            activeActivityIds = runtimeService.getActiveActivityIds(processId);
        } else {
            //3.2. 已经结束的流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();
            processDefinitionId = historicProcessInstance.getProcessDefinitionId();
            //3.3. 获取结束节点列表
            List<HistoricActivityInstance> historicEnds = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(processId).activityType(BpmnXMLConstants.ELEMENT_EVENT_END).list();
            List<String> finalActiveActivityIds = activeActivityIds;
            historicEnds.forEach(historicActivityInstance -> finalActiveActivityIds.add(historicActivityInstance.getActivityId()));
        }
        //4. 获取bpmnModel对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        //5. 生成图片流
        InputStream inputStream = flowProcessDiagramGenerator.generateDiagram(bpmnModel, activeActivityIds, highLightedFlows);
        //6. 转化成byte便于网络传输
        byte[] datas = IoUtil.readInputStream(inputStream, "image inputStream name");
        return datas;
    }

    /**
     * 根据流程实例id和任务id更新或新增意见保存
     *
     * @param processId
     * @param taskId
     * @param comment
     * @param userId
     */
    private void saveComment(String processId, String taskId, String comment, String userId) {
        ActHiComment actHiComment = actHiCommentService.getOne(
                Wrappers.lambdaQuery(ActHiComment.builder().procInstId(processId).taskId(taskId).type(Contansts.COMMENT).build())
        );
        if (Objects.isNull(actHiComment)) {
            actHiComment = ActHiComment.builder()
                    .id(UUID.randomUUID().toString())
                    .procInstId(processId).taskId(taskId).message(comment).type(Contansts.COMMENT).time(LocalDateUtil.dateFormat(new Date(), LocalDateUtil.FORMAT_PATTERN1)).userId(userId)
                    .build();
            actHiCommentService.save(actHiComment);
            return;
        }
        actHiComment.setMessage(comment);
        actHiComment.setUserId(userId);
        actHiComment.setTime(LocalDateUtil.dateFormat(new Date(), LocalDateUtil.FORMAT_PATTERN1));
        actHiCommentService.updateById(actHiComment);
    }


    /**
     * 业务文本数据转换为Map<String,Object>格式输出
     *
     * @param data
     * @return
     */
    private IPage<Map<String, Object>> transfer(IPage<Map<String, Object>> data) {
        Optional.ofNullable(data.getRecords()).orElseGet(ArrayList::new).stream().forEach(d -> {
            String text = (String) d.remove("text");
            Map<String, Object> map = JSONObject.parseObject(text, new TypeReference<Map<String, Object>>() {
            });
            d.putAll(map);
        });
        return data;
    }

    /**
     * 获取流程图
     *
     * @param processDefId
     * @return
     */
    public byte[] flowDefImage(String processDefId) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefId);
        InputStream inputStream = flowProcessDiagramGenerator.generateDiagram(bpmnModel, new ArrayList<>(), new ArrayList<>());
        byte[] datas = IoUtil.readInputStream(inputStream, "image inputStream name");
        return datas;
    }
}
